package com.dsunsoft.rtu.core.interceptor;

import com.dsunsoft.rtu.common.util.SecurityUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.binding.MapperMethod;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.*;
import org.springframework.util.CollectionUtils;

import java.lang.reflect.Field;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 自定义mybatis拦截器
 *
 * @author ygm
 */
@Slf4j
@Intercepts({@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})})
public class MybatisInterceptor implements Interceptor {
    /**
     * 创建人
     */
    private static final String CREATED_BY = "createdBy";
    /**
     * 创建时间
     */
    private static final String CREATED_DATE = "createdDate";
    /**
     * 最后修改人
     */
    private static final String LAST_MODIFIED_BY = "lastModifiedBy";
    /**
     * 最后修改时间
     */
    private static final String LAST_MODIFIED_DATE = "lastModifiedDate";
    /**
     * 类属性缓存
     */
    private static final Map<Class<?>, List<Field>> CLASS_FIELD_CACHE = new ConcurrentHashMap<>();

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[0];
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        Object parameter = invocation.getArgs()[1];
        // org.apache.ibatis.binding.MapperMethod$ParamMap dao层 @Param 参数
        if (parameter != null && !parameter.getClass().equals(MapperMethod.ParamMap.class)) {
            Date now = new Date(); // 当前时间
            String userId = SecurityUtils.getCurrentUserLogin(); // 用户id
            // 反射获取类的审计字段(创建人,创建时间,修改人,修改时间)和主键ID,使用 ConcurrentHashMap 缓存
            List<Field> fieldList = getFieldList(parameter.getClass());

            if (!CollectionUtils.isEmpty(fieldList)) {
                if (SqlCommandType.UPDATE == sqlCommandType) {
                    for (Field field : fieldList) {
                        if (LAST_MODIFIED_BY.equals(field.getName())) {
                            setValue(parameter, field, userId);
                        }
                        if (LAST_MODIFIED_DATE.equals(field.getName())) {
                            setValue(parameter, field, now);
                        }
                    }
                } else if (SqlCommandType.INSERT == sqlCommandType) {
                    for (Field field : fieldList) {
                        if (CREATED_BY.equals(field.getName())) {
                            setValue(parameter, field, userId);
                        }
                        if (CREATED_DATE.equals(field.getName())) {
                            setValue(parameter, field, now);
                        }
                        if (LAST_MODIFIED_BY.equals(field.getName())) {
                            setValue(parameter, field, userId);
                        }
                        if (LAST_MODIFIED_DATE.equals(field.getName())) {
                            setValue(parameter, field, now);
                        }
                    }
                }
            }
        }
        return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
        return Plugin.wrap(target, this);
    }

    @Override
    public void setProperties(Properties properties) {
    }

    /**
     * 获取该类的审计字段(创建人,创建时间,修改人,修改时间)和主键ID,使用缓存
     *
     * @param clazz 类
     * @return 字段列表
     */
    private static List<Field> getFieldList(Class<?> clazz) {
        if (Objects.isNull(clazz)) {
            return Collections.emptyList();
        }
        if (CLASS_FIELD_CACHE.containsKey(clazz)) {
            return CLASS_FIELD_CACHE.get(clazz);
        }
        List<Field> fieldList = new ArrayList<>();
        Collections.addAll(fieldList, clazz.getDeclaredFields());
        Class<?> currentClass = clazz.getSuperclass();
        while (currentClass != null) {
            Field[] declaredFields = currentClass.getDeclaredFields();
            Collections.addAll(fieldList, declaredFields);
            currentClass = currentClass.getSuperclass();
        }
        // 只获取审计字段
        fieldList = fieldList.stream().filter(p -> CREATED_BY.equals(p.getName()) || CREATED_DATE.equals(p.getName())
                || LAST_MODIFIED_BY.equals(p.getName()) || LAST_MODIFIED_DATE.equals(p.getName())).collect(Collectors.toList());

        CLASS_FIELD_CACHE.put(clazz, fieldList);
        return fieldList;
    }

    /**
     * 设置属性值,有值不修改
     *
     * @param obj   对象
     * @param field 字段
     * @param value 值
     * @throws IllegalAccessException ex
     */
    private static void setValue(Object obj, Field field, Object value) throws IllegalAccessException {
        field.setAccessible(true);
        field.set(obj, value);
    }
}
